#define quickread
#ifdef quickread
#include <cstdio> 
template <typename T>
inline void read(T& x)
{
    int c=getchar(), f=1; x=0;
    while(c<'0'||'9'<c) {if(c=='-') f=-1; c=getchar();}
    while('0'<=c&&c<='9') 
        x=(x<<3)+(x<<1)+c-'0', c=getchar();
    x*=f;
}
template <typename T>
inline void quickwrite(T x)
{
    if(x<0) putchar('-'), x*=-1;
    if(x>=10) quickwrite(x/10);
    putchar(x%10+'0');
}
#else 
#include <iostream>
#endif
const int INF=0x3f3f3f3f;
#include <cstring>
#include <algorithm>
using namespace std;
#define DEBUG
const int N=100010, M=N*3;

int n, m, Q, new_n; //方点
int h1[N], h2[N], w[M], e[M], ne[M], idx;
int dfn[N], low[N], cnt; //dfs序 最早能回溯到的祖先节点访问顺序
int s[N], stot[N], fu[N], fw[N];
/*  环上节点到环上某一点的距离
    环上节点到环上起点的总距离
    父节点
    父节点到当前节点的边权
*/
int fa[N];
int circle[N], dist[N], q[N]; 
int f[N], d[N];
//环展开后环上节点的子树路径数组(即dist[]) 
//dist[u]:从u出发，经过其子树的最长路径长度
int ans;

void AddEdge(int h[], int a, int b, int c)
{
    e[idx]=b, w[idx]=c, ne[idx]=h[a], h[a]=idx++; 
}

//重新构建环在新图的连接方式
void build_circle(int x, int y, int z)
{
    int sum=z;
    for(int k=y; k!=x; k=fu[k])
        s[k]=sum, sum+=fw[k]; //计算环上节点到环上某一点的距离
    s[x]=stot[x]=sum;
    AddEdge(h2, x, ++new_n, 0); //添加一个新的虚拟节点
    for(int k=y; k!=x; k=fu[k])
    {
        stot[k]=sum;
        AddEdge(h2, new_n, k, min(s[k], sum-s[k])); //边权为该节点到环上起点的最短路径距离
    }
}

//搜换，把不在环上的边加到新图h2中
void Tarjan(int u, int from) //当前遍历到那个点 从哪条边来的
{
    dfn[u]=low[u]=++cnt;
    for(int i=h1[u]; ~i; i=ne[i])
    {
        int v=e[i];
        if(!dfn[v])
        {
            fu[v]=u, fw[v]=w[i]; //父节点 父节点到当前节点的边权
            Tarjan(v, i);
            low[u]=min(low[u], low[v]); //v可能回溯上去
            if(dfn[u]<low[v]) AddEdge(h2, u, v, w[i]); //割边 添加到块树
        }
        else if(i!=(from^1)) low[u]=min(low[u], dfn[v]); //非父边
    }

    //处理环
    for(int i=h1[u]; ~i; i=ne[i])
    {
        int v=e[i];
        if(dfn[u]<dfn[v]&&fu[v]!=u) //u是环起点
            build_circle(u, v, w[i]);
    }
}   

int dfs(int u)
{
    int d1=0, d2=0; //最长路径长度 次长路径长度
    for(int i=h2[u]; ~i; i=ne[i])
    {
        int v=e[i];
        int t=dfs(v)+w[i];
        if(t>=d1) d2=d1, d1=t;
        else if(t>d2) d2=t;
    }

    dist[u]=d1; //当前节点的最长路径(单链)
    if(u<=n) ans=max(ans, d1+d2); //u是原图中的点
    else
    {
        int sz=0;
        circle[sz++]=-INF; //哨兵
        for(int i=h2[u]; ~i; i=ne[i]) //记录所有子路径长度
            circle[sz++]=dist[e[i]];
        // 复制数组处理环形结构
        for(int i=0; i<sz; i++) circle[sz+i]=circle[i];
        
        int hh=0, tt=-1;
        for(int i=0; i<(sz<<1); i++) {
            if(hh<=tt&&i-q[hh]>sz>>1) hh++;
            if(hh<=tt) ans=max(ans, circle[i]+i+circle[q[hh]]-q[hh]); // 计算最大子段和
            // 维护单调队列：circle[i]-i递减
            while(hh<=tt&&circle[q[tt]]-q[tt]<=circle[i]-i) tt--;
            q[++tt]=i;
        }
    }
    return dist[u];
}

void init()
{
    read(n), read(m);
    new_n=n; //方节点起始编号
    memset(h1, -1, sizeof h1);
    memset(h2, -1, sizeof h2);
    while(m--)
    {
        int k, x, y; read(k), read(x);
        for(int i=0; i<k-1; i++)
        {
            read(y);
            AddEdge(h1, x, y, 1); AddEdge(h1, y, x, 1);
            x=y;
        }
    }
}

void solve()
{
    init();

    Tarjan(1, -1);
    dfs(1);
    
    quickwrite(ans), puts("");
}

// #undef DEBUG
signed main()
{
    #ifdef DEBUG
        freopen("./in.txt", "r", stdin);
        freopen("./out.txt", "w", stdout);
    #endif
    #ifndef quickread
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    #endif

    int T=1; //scanf("%d", &T);
    while(T--) 
    {
        solve();
    }
    return 0;
}
